/*-------------------------------------------------------------------------
 *
 * ipci.c
 *	  POSTGRES 进程间通信初始化代码。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/storage/ipc/ipci.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/clog.h"
#include "access/commit_ts.h"
#include "access/heapam.h"
#include "access/multixact.h"
#include "access/nbtree.h"
#include "access/subtrans.h"
#include "access/syncscan.h"
#include "access/twophase.h"
#include "access/xlogprefetcher.h"
#include "access/xlogrecovery.h"
#include "commands/async.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "postmaster/bgworker_internals.h"
#include "postmaster/bgwriter.h"
#include "postmaster/postmaster.h"
#include "replication/logicallauncher.h"
#include "replication/origin.h"
#include "replication/slot.h"
#include "replication/walreceiver.h"
#include "replication/walsender.h"
#include "storage/bufmgr.h"
#include "storage/dsm.h"
#include "storage/ipc.h"
#include "storage/pg_shmem.h"
#include "storage/pmsignal.h"
#include "storage/predicate.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/sinvaladt.h"
#include "storage/spin.h"
#include "utils/snapmgr.h"

/* GUCs */
int			shared_memory_type = DEFAULT_SHARED_MEMORY_TYPE;

shmem_startup_hook_type shmem_startup_hook = NULL;

static Size total_addin_request = 0;

/*
 * RequestAddinShmemSpace
 *		请求为可加载模块分配额外的共享内存空间。
 *
 * 这只能通过加载到 postmaster 中的 shared_preload_libraries 的
 * shmem_request_hook 调用。来自其他地方的调用将失败。
 */
void RequestAddinShmemSpace(Size fc_size)
{
	if (!process_shmem_requests_in_progress)
		elog(FATAL, "cannot request additional shared memory outside shmem_request_hook");
	total_addin_request = add_size(total_addin_request, fc_size);
}

/*
 * CalculateShmemSize
 *		计算所需的共享内存量和信号量数量。
 *
 * 如果 num_semaphores 不是 NULL，它将被设置为所需的信号量
 * 数量。
 */
Size CalculateShmemSize(int *fc_num_semaphores)
{
	Size		fc_size;
	int			fc_numSemas;

	/* 计算我们需要的信号量数量 */
	fc_numSemas = ProcGlobalSemas();
	fc_numSemas += SpinlockSemas();

	/* 如果调用者请求，则返回信号量的数量 */
	if (fc_num_semaphores)
		*fc_num_semaphores = fc_numSemas;

	/*
	 * Postgres 共享内存块的大小通过对大块的中等准确估计来估算，
	 * 加上 100K 用于那些小到不值得估算的东西。
	 *
	 * 我们特别注意确保总大小请求不会溢出 size_t。
	 * 如果这一点通过了，我们在实际分配阶段就不需要如此小心。
	 */
	fc_size = 100000;
	fc_size = add_size(fc_size, PGSemaphoreShmemSize(fc_numSemas));
	fc_size = add_size(fc_size, SpinlockSemaSize());
	fc_size = add_size(fc_size, hash_estimate_size(SHMEM_INDEX_SIZE,
											 sizeof(ShmemIndexEnt)));
	fc_size = add_size(fc_size, dsm_estimate_size());
	fc_size = add_size(fc_size, BufferShmemSize());
	fc_size = add_size(fc_size, LockShmemSize());
	fc_size = add_size(fc_size, PredicateLockShmemSize());
	fc_size = add_size(fc_size, ProcGlobalShmemSize());
	fc_size = add_size(fc_size, XLogPrefetchShmemSize());
	fc_size = add_size(fc_size, XLOGShmemSize());
	fc_size = add_size(fc_size, XLogRecoveryShmemSize());
	fc_size = add_size(fc_size, CLOGShmemSize());
	fc_size = add_size(fc_size, CommitTsShmemSize());
	fc_size = add_size(fc_size, SUBTRANSShmemSize());
	fc_size = add_size(fc_size, TwoPhaseShmemSize());
	fc_size = add_size(fc_size, BackgroundWorkerShmemSize());
	fc_size = add_size(fc_size, MultiXactShmemSize());
	fc_size = add_size(fc_size, LWLockShmemSize());
	fc_size = add_size(fc_size, ProcArrayShmemSize());
	fc_size = add_size(fc_size, BackendStatusShmemSize());
	fc_size = add_size(fc_size, SInvalShmemSize());
	fc_size = add_size(fc_size, PMSignalShmemSize());
	fc_size = add_size(fc_size, ProcSignalShmemSize());
	fc_size = add_size(fc_size, CheckpointerShmemSize());
	fc_size = add_size(fc_size, AutoVacuumShmemSize());
	fc_size = add_size(fc_size, ReplicationSlotsShmemSize());
	fc_size = add_size(fc_size, ReplicationOriginShmemSize());
	fc_size = add_size(fc_size, WalSndShmemSize());
	fc_size = add_size(fc_size, WalRcvShmemSize());
	fc_size = add_size(fc_size, PgArchShmemSize());
	fc_size = add_size(fc_size, ApplyLauncherShmemSize());
	fc_size = add_size(fc_size, SnapMgrShmemSize());
	fc_size = add_size(fc_size, BTreeShmemSize());
	fc_size = add_size(fc_size, SyncScanShmemSize());
	fc_size = add_size(fc_size, AsyncShmemSize());
	fc_size = add_size(fc_size, StatsShmemSize());
#ifdef EXEC_BACKEND
	fc_size = add_size(fc_size, ShmemBackendArraySize());
#endif

	/* 从预加载库中包含额外请求的共享内存 */
	fc_size = add_size(fc_size, total_addin_request);

	/* 不妨将其四舍五入到典型页面大小的倍数 */
	fc_size = add_size(fc_size, 8192 - (fc_size % 8192));

	return fc_size;
}


/*
 * CreateSharedMemoryAndSemaphores
 *		创建和初始化共享内存和信号量。
 *
 * 这是由后端管理器或独立的后端调用的。
 * 它也由从后端管理器派生的后端在
 * EXEC_BACKEND 情况下调用。在后一种情况下，共享内存段
 * 已经存在并被物理附加，但是我们必须
 * 在本地内存中初始化引用共享结构的指针，
 * 因为我们没有从后端管理器继承到正确的指针值
 * 就像我们在 fork() 场景中那样。最简单的方法是运行
 * 之前的相同代码。（请注意，调用的例程主要
 * 检查 IsUnderPostmaster，而不是 EXEC_BACKEND，以检测此情况。
 * 这有点浪费代码，并且可以进行清理。）
 */
void CreateSharedMemoryAndSemaphores(void)
{
	PGShmemHeader *fc_shim = NULL;

	if (!IsUnderPostmaster)
	{
		PGShmemHeader *fc_seghdr;
		Size		fc_size;
		int			fc_numSemas;

		/* 计算共享内存块的大小 */
		fc_size = CalculateShmemSize(&fc_numSemas);
		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", fc_size);

		/*
		 * 创建 shmem 段
		 */
		fc_seghdr = PGSharedMemoryCreate(fc_size, &fc_shim);

		InitShmemAccess(fc_seghdr);

		/*
		 * 创建信号量
		 */
		PGReserveSemaphores(fc_numSemas);

		/*
		 * 如果禁用了自旋锁，初始化仿真层（依赖于信号量，
		 * 所以这里的顺序很重要）。
		 */
#ifndef HAVE_SPINLOCKS
		SpinlockSemaInit();
#endif
	}
	else
	{
		/*
		 * 我们正在重新附加到现有的共享内存段。这
		 * 仅应在 EXEC_BACKEND 情况下到达。
		 */
#ifndef EXEC_BACKEND
		elog(PANIC, "should be attached to shared memory already");
#endif
	}

	/*
	 * 设置共享内存分配机制
	 */
	if (!IsUnderPostmaster)
		InitShmemAllocation();

	/*
	 * 现在初始化 LWLocks，它们进行共享内存分配，并且
	 * 对 InitShmemIndex 是必要的。
	 */
	CreateLWLocks();

	/*
	 * 设置 shmem.c 索引哈希表
	 */
	InitShmemIndex();

	dsm_shmem_init();

	/*
	 * 设置 xlog、clog 和缓冲区
	 */
	XLOGShmemInit();
	XLogPrefetchShmemInit();
	XLogRecoveryShmemInit();
	CLOGShmemInit();
	CommitTsShmemInit();
	SUBTRANSShmemInit();
	MultiXactShmemInit();
	InitBufferPool();

	/*
	 * 设置锁管理器
	 */
	InitLocks();

	/*
	 * 设置谓词锁管理器
	 */
	InitPredicateLocks();

	/*
	 * 设置进程表
	 */
	if (!IsUnderPostmaster)
		InitProcGlobal();
	CreateSharedProcArray();
	CreateSharedBackendStatus();
	TwoPhaseShmemInit();
	BackgroundWorkerShmemInit();

	/*
	 * 设置共享无效消息传递
	 */
	CreateSharedInvalidationState();

	/*
	 * 设置进程间信号机制
	 */
	PMSignalShmemInit();
	ProcSignalShmemInit();
	CheckpointerShmemInit();
	AutoVacuumShmemInit();
	ReplicationSlotsShmemInit();
	ReplicationOriginShmemInit();
	WalSndShmemInit();
	WalRcvShmemInit();
	PgArchShmemInit();
	ApplyLauncherShmemInit();

	/*
	 * 设置需要一些共享内存空间的其他模块
	 */
	SnapMgrInit();
	BTreeShmemInit();
	SyncScanShmemInit();
	AsyncShmemInit();
	StatsShmemInit();

#ifdef EXEC_BACKEND

	/*
	 * 分配 win32 共享后端数组
	 */
	if (!IsUnderPostmaster)
		ShmemBackendArrayAllocation();
#endif

	/* 初始化动态共享内存设施。 */
	if (!IsUnderPostmaster)
		dsm_postmaster_startup(fc_shim);

	/*
	 * 现在让可加载模块有机会设置它们的共享内存分配
	 */
	if (shmem_startup_hook)
		shmem_startup_hook();
}

/*
 * InitializeShmemGUCs
 *
 * 此函数初始化与当前配置所需的共享内存量相关的运行时计算 GUC。
 */
void InitializeShmemGUCs(void)
{
	char		fc_buf[64];
	Size		fc_size_b;
	Size		fc_size_mb;
	Size		fc_hp_size;

	/*
	 * 计算共享内存大小并向上取整到最近的兆字节。
	 */
	fc_size_b = CalculateShmemSize(NULL);
	fc_size_mb = add_size(fc_size_b, (1024 * 1024) - 1) / (1024 * 1024);
	sprintf(fc_buf, "%zu", fc_size_mb);
	SetConfigOption("shared_memory_size", fc_buf,
					PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT);

	/*
	 * 计算所需的巨大页数。
	 */
	GetHugePageSize(&fc_hp_size, NULL);
	if (fc_hp_size != 0)
	{
		Size		fc_hp_required;

		fc_hp_required = add_size(fc_size_b / fc_hp_size, 1);
		sprintf(fc_buf, "%zu", fc_hp_required);
		SetConfigOption("shared_memory_size_in_huge_pages", fc_buf,
						PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT);
	}
}
